import { unrefElement } from './shared';
import { useEventListener } from './useEventListener';
// 判断是不是ios设备
const isIOS = window.navigator && window.navigator.userAgent && /iP(ad|hone|od)/.test(window.navigator.userAgent);
// 标记是否注册过事件
let iOSWorkaround = false;

export function onClickOutside(target, handler, options = {}) {
  const {
    ignore = [], capture = true
  } = options;
  // 解决ios上点击事件不会正常冒泡的bug
  // Fixes: https://github.com/vueuse/vueuse/issues/1520
  // How it works: https://stackoverflow.com/a/39712411
  if (isIOS && !iOSWorkaround) {
    iOSWorkaround = true;
    Array.from(window.document.body.children).forEach((el) => el.addEventListener('click', noop));
  }

  // 为 true 才会执行 handler
  let shouldListen = true;
  // 需要忽略的
  // string先获取dom
  // 拿到dom集合 是自己本身或者在冒泡的路径上
  const shouldIgnore = (event) => ignore.some((target2) => {
    if (typeof target2 === 'string') {
      return Array.from(window.document.querySelectorAll(target2)).some((el) => el === event.target || event.composedPath().includes(el));
    }
    const el = unrefElement(target2);
    return el && (event.target === el || event.composedPath().includes(el));
  });
  // 先执行 pointerdown 再 click
  // shouldListen为 true 才会执行handler
  const listener = (event) => {
    const el = unrefElement(target);
    // 是自己本身或者在冒泡的路径上 return
    if (!el || el === event.target || event.composedPath().includes(el)) return;
    // event.detail
    // 数字值，表示短时间内发生的点击次数。
    // 注释：ondblclick 事件的返回值始终为 "2"
    // 注释：onmouseover 或 onmouseout 事件的返回值始终为 "0"
    if (event.detail === 0) shouldListen = !shouldIgnore(event);
    if (!shouldListen) {
      shouldListen = true;
      return;
    }
    handler(event);
  };
  const cleanup = [
    // 监听 window 的点击事件
    useEventListener(window, 'click', listener, { passive: true, capture }),
    // 鼠标、触摸、触控笔 按下时
    useEventListener(window, 'pointerdown', (e) => {
      const el = unrefElement(target);
      // 目标dom不在点击冒泡的路径中 并且不在需要忽略的列表中
      // shouldListen = 如果点击的是自身或者是自身的子元素 - 忽略列表除外
      if (el) shouldListen = !e.composedPath().includes(el) && !shouldIgnore(e);
    }, { passive: true })
  ].filter(Boolean);

  const stop = () => cleanup.forEach((fn) => fn());
  return stop;
}
